﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using AForge;
using AForge.Controls;
using System.Threading;

namespace CarEvaluationANN
{
    public partial class TestForm : Form
    {
        #region Props&Fields
        private NeuralNetworkController nnController;
        private CSVTable _csvTable;
        #endregion

        #region Constructors
        public TestForm()
        {
            InitializeComponent();
            ofdDataSetFile.InitialDirectory = Application.StartupPath;
            nnController = new NeuralNetworkController(new List<DataSetUnit>());
            txtCycles.Text = nnController.Cycles.ToString();
            txtLearningRate.Text = nnController.LearningRate.ToString();
            txtNeuronCount.Text = nnController.NeuronCount.ToString();
            txtAlpha.Text = nnController.SigmoidAlphaValue.ToString();
            txtMaxError.Text = nnController.LearningErrorLimit.ToString();
            txtMomentum.Text = nnController.Momentum.ToString();
            errorChart.AddDataSeries("error", Color.Red, Chart.SeriesType.Line, 1);
        }
        #endregion

        #region Methods
        private delegate void SetTextCallback(Control control, string text);
        private void SetText(Control control, string text)
        {
            if (control.InvokeRequired)
            {
                var d = new SetTextCallback(SetText);
                Invoke(d, new object[] { control, text });
            }
            else
            {
                control.Text = text;
            }
        }

        private delegate void UpdateChartCallback(double[,] errors);
        private void UpdateChart(double[,] errors)
        {
            if (errorChart.InvokeRequired)
            {
                Invoke(new UpdateChartCallback(UpdateChart), errors);
            }
            else
            {
                errorChart.RangeX = new Range(0, errors.Length - 1);
                errorChart.UpdateDataSeries("error", errors);
            }
        }

        private void Start()
        {
            if (String.IsNullOrEmpty(ofdDataSetFile.FileName))
            {
                MessageBox.Show("Dosya Seçiniz!!!");
                return;
            }

            var lines = File.ReadAllLines(ofdDataSetFile.FileName);
            _csvTable = new CSVTable(lines);
            var inputHeaderList =
                _csvTable.HeaderRow.GetRow().Where(t => Regex.IsMatch(t, @"X\d+", RegexOptions.IgnoreCase)).ToList();
            var outputHeaderList =
                _csvTable.HeaderRow.GetRow().Where(t => Regex.IsMatch(t, @"Y\d+", RegexOptions.IgnoreCase)).ToList();

            var ts = new List<DataSetUnit>();
            for (var i = 1; i < _csvTable.RowCount; i++)
            {
                var inputs = new List<double>();
                var outputs = new List<double>();
                inputHeaderList.ForEach(x => inputs.Add(UtilityConvert.GetDouble(_csvTable[i, x])));
                outputHeaderList.ForEach(y => outputs.Add(UtilityConvert.GetDouble(_csvTable[i, y])));

                ts.Add(new DataSetUnit(inputs.ToArray(), outputs.ToArray()));
            }
            nnController = new NeuralNetworkController(ts);
            nnController.LearningRate = UtilityConvert.GetDouble(txtLearningRate.Text);
            nnController.Cycles = UtilityConvert.GetInt32(txtCycles.Text);
            nnController.NeuronCount = UtilityConvert.GetInt32(txtNeuronCount.Text);
            nnController.SigmoidAlphaValue = UtilityConvert.GetDouble(txtAlpha.Text);
            nnController.LearningErrorLimit = UtilityConvert.GetDouble(txtMaxError.Text);
            nnController.Momentum = UtilityConvert.GetDouble(txtMomentum.Text);
            nnController.SaveStatisticsToFile = cbSaveFile.Checked;

            nnController.EpochEnded += (o, e) =>
                                           {
                                               SetText(txtCurrentError,
                                                       e.Error.ToString());
                                               SetText(txtCurrentIteration, nnController.ErrorList.Count.ToString());

                                               var errors = new double[nnController.ErrorList.Count, 2];

                                               for (int i = 0, n = nnController.ErrorList.Count; i < n; i++)
                                               {
                                                   errors[i, 0] = i;
                                                   errors[i, 1] = (double)nnController.ErrorList[i];
                                               }
                                               UpdateChart(errors);
                                           };
            EnableControls(false);
            nnController.Train();
            EnableControls(true);
            PrintErrorChart();
        }

        private Thread _thread1;

        private delegate void EnableCallback(bool enabled);
        private void EnableControls(bool enabled)
        {
            if (InvokeRequired)
            {
                var d = new EnableCallback(EnableControls);
                Invoke(d, new object[] { enabled });
            }
            else
            {
                gboxInputs.Enabled = enabled;
                btnStart.Enabled = enabled;
                btnStop.Enabled = !enabled;
                cbSaveFile.Enabled = enabled;
            }
        }

        private delegate void PrintControlCallback(Control control, string filename);
        private void PrintControl(Control control, string filename)
        {
            if (control.InvokeRequired)
            {
                Invoke(new PrintControlCallback(PrintControl), control, filename);
            }
            else
            {
                var g = control.CreateGraphics();
                //new bitmap object to save the image        
                var bmp = new Bitmap(control.Width, control.Height);
                //Drawing control to the bitmap        
                control.DrawToBitmap(bmp, new Rectangle(0, 0, control.Width, control.Height));
                bmp.Save(filename, ImageFormat.Jpeg);
                bmp.Dispose();
            }
        }

        private void PrintErrorChart()
        {
            var filePath = String.Format("./Statistics/{0}.jpg", nnController.CurrentGuid);
            PrintControl(errorChart, filePath);
        }

        private void TestDataSet()
        {
            if (String.IsNullOrEmpty(ofdTestSet.FileName))
            {
                MessageBox.Show("Dosya Seçiniz!!!");
                return;
            }

            var lines = File.ReadAllLines(ofdTestSet.FileName);
            _csvTable = new CSVTable(lines);
            var inputHeaderList =
                _csvTable.HeaderRow.GetRow().Where(t => Regex.IsMatch(t, @"X\d+", RegexOptions.IgnoreCase)).ToList();
            var outputHeaderList =
                _csvTable.HeaderRow.GetRow().Where(t => Regex.IsMatch(t, @"Y\d+", RegexOptions.IgnoreCase)).ToList();


            var ts = new List<DataSetUnit>();
            for (var i = 1; i < _csvTable.RowCount; i++)
            {
                var inputs = new List<double>();
                var outputs = new List<double>();
                inputHeaderList.ForEach(x => inputs.Add(UtilityConvert.GetDouble(_csvTable[i, x])));
                outputHeaderList.ForEach(y => outputs.Add(UtilityConvert.GetDouble(_csvTable[i, y])));

                ts.Add(new DataSetUnit(inputs.ToArray(), outputs.ToArray()));
            }
            var res = nnController.Test(ts);
            MessageBox.Show(res);
        }

        #endregion

        #region Events
        private void btnBrowse_Click(object sender, EventArgs e)
        {
            ofdDataSetFile.ShowDialog();
        }

        private void ofdDataSetFile_FileOk(object sender, CancelEventArgs e)
        {
            txtDataSetFile.Text = ofdDataSetFile.FileName;
        }

        private void btnTest_Click(object sender, EventArgs e)
        {
            ofdTestSet.ShowDialog();
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            _thread1 = new Thread(Start);
            _thread1.Start();
        }

        private void TestForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (_thread1 != null && _thread1.IsAlive)
            {
                while (!_thread1.Join(100))
                    Application.DoEvents();
            }
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            _thread1.Abort();
            EnableControls(true);
        }

        private void btnLoad_Click(object sender, EventArgs e)
        {
            ofdNetworkBinary.ShowDialog();
        }

        private void ofdNetworkBinary_FileOk(object sender, CancelEventArgs e)
        {
            nnController.Load(ofdNetworkBinary.FileName);
        }

        private void ofdTestSet_FileOk(object sender, CancelEventArgs e)
        {
            TestDataSet();
        }
        #endregion
    }
}